#include "main.h"
#include "opengl_3.h"

OpenGLContext openglContext; // Our OpenGL Context class
bool keys[256];

bool running = true; // Whether or not the application is currently running

HINSTANCE hInstance; // The HINSTANCE of this application
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Standard window callback


// maximum mumber of lines the output console should have
static const WORD MAX_CONSOLE_LINES = 500;


void RedirectIOToConsole()
{
    int hConHandle;
    long lStdHandle;
    CONSOLE_SCREEN_BUFFER_INFO coninfo;
    FILE *fp;

// allocate a console for this app
    AllocConsole();

// set the screen buffer to be big enough to let us scroll text
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&coninfo);
    coninfo.dwSize.Y = MAX_CONSOLE_LINES;
    SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),coninfo.dwSize);

// redirect unbuffered STDOUT to the console
    lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);

    fp = _fdopen( hConHandle, "w" );

    *stdout = *fp;

    setvbuf( stdout, NULL, _IONBF, 0 );

// redirect unbuffered STDIN to the console

    lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);

    fp = _fdopen( hConHandle, "r" );
    *stdin = *fp;
    setvbuf( stdin, NULL, _IONBF, 0 );

// redirect unbuffered STDERR to the console
    lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);

    fp = _fdopen( hConHandle, "w" );

    *stderr = *fp;

    setvbuf( stderr, NULL, _IONBF, 0 );

// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
// point to console as well
    ios::sync_with_stdio();
}



/**
  WndProc is a standard method used in Win32 programming for handling Window messages. Here we
  handle our window resizing and tell our OpenGLContext the new window size.
*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  switch (message) {
    case WM_SIZE: // If our window is resizing
    {
      openglContext.reshapeWindow(LOWORD(lParam), HIWORD(lParam)); // Send the new window size to our OpenGLContext
      break;
    }

    case WM_KEYDOWN:							// Is A Key Being Held Down?
    {
      keys[wParam] = TRUE;					// If So, Mark It As TRUE
      return 0;								// Jump Back
    }

    case WM_KEYUP:								// Has A Key Been Released?
    {
      keys[wParam] = FALSE;					// If So, Mark It As FALSE
      return 0;								// Jump Back
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      break;
    }
  }

  return DefWindowProc(hWnd, message, wParam, lParam);
}

/**
  createWindow is going to create our window using Windows API calls. It is then going to
  create our OpenGL context on the window and then show our window, making it visible.
*/
bool createWindow(LPCWSTR title, int width, int height) {
  WNDCLASS windowClass;
  HWND hWnd;
  DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;

  hInstance = GetModuleHandle(NULL);

  windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  windowClass.lpfnWndProc = (WNDPROC) WndProc;
  windowClass.cbClsExtra = 0;
  windowClass.cbWndExtra = 0;
  windowClass.hInstance = hInstance;
  windowClass.hIcon = LoadIcon(NULL, IDI_WINLOGO);
  windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  windowClass.hbrBackground = NULL;
  windowClass.lpszMenuName = NULL;
  windowClass.lpszClassName = title;

  if (!RegisterClass(&windowClass)) {
    return false;
  }

  hWnd = CreateWindowEx(dwExStyle, title, title, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, width, height, NULL, NULL, hInstance, NULL);

  openglContext.create30Context(hWnd); // Create our OpenGL context on the given window we just created

  ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);

  return true;
}

/**
  WinMain is the main entry point for Windows based applications as opposed to 'main' for console
  applications. Here we will make the calls to create our window, setup our scene and then
  perform our 'infinite' loop which processes messages and renders.
*/
int WINAPI WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR    lpCmdLine,
                     int       nCmdShow) {
  MSG msg;


    
  RedirectIOToConsole();
  /**
    The following 6 lines of code do conversion between char arrays and LPCWSTR variables
    which are used in the Windows API.
  */
  char *orig = "OpenGL 3 Project"; // Our windows title
  size_t origsize = strlen(orig) + 1;
    const size_t newsize = 100;
    size_t convertedChars = 0;
    wchar_t wcstring[newsize];
    mbstowcs_s(&convertedChars, wcstring, origsize, orig, _TRUNCATE);

  createWindow(wcstring, 1024, 768); // Create our OpenGL window

  openglContext.setupScene(); // Setup our OpenGL scene

  openglContext.generatePeon();

  /*while (running) {
    openglContext.updatePeon();  
  }*/

  /**
    This is our main loop, it continues for as long as running is true
  */
  while (running)
  {
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // If we have a message to process, process it
      if (msg.message == WM_QUIT) {
        running = false; // Set running to false if we have a message to quit
      }
      else {      
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
    }
    else { // If we don't have a message to process
   
      openglContext.updateKeyboard(keys);      
      openglContext.updatePeon();  
      openglContext.renderScene(); // Render our scene (which also handles swapping of buffers)
    }
  }

  return (int) msg.wParam;
}
